---
title: Linking
description: Learn how to use Linking to handle a URL based on the URL scheme.
---

import { BookOpen02Icon } from '@expo/styleguide-icons/outline/BookOpen02Icon';

import { BoxLink } from '~/ui/components/BoxLink';
import { ConfigReactNative } from '~/ui/components/ConfigSection';
import { SnackInline, Terminal } from '~/ui/components/Snippet';

URLs are the most powerful way to launch native applications. Native operating systems like macOS, Android, iOS, Windows, and so on, have built-in link handling which chooses an app to handle a URL based on the _URL scheme_. The most common _URL schemes_ are `https` and `http` which are delegated to web browsers like Chrome, or Safari. Native apps, like the ones built with React Native, can implement any _URL scheme_, and the JavaScript React layer can handle the URL used to launch the corresponding native app.

## Linking from your app

The [`expo-linking`][expo-linking] API universally abstracts over native linking APIs (like `window.history` on web).

```ts
import * as Linking from 'expo-linking';

Linking.openURL('https://expo.dev');
```

{/* TODO(EvanBacon): Maybe we should move `<A />` to `expo-linking`. */}

Web browsers have additional link functionality like right-click to copy, and hover to preview. You can use the package [`@expo/html-elements`](https://www.npmjs.com/package/@expo/html-elements) to get a universal `<A />` element:

<Terminal cmd={['$ npx expo install @expo/html-elements']} />

```tsx
import { A } from '@expo/html-elements';

export default function App() {
  return <A href="https://google.com">Go to Google</A>;
}
```

This renders an `<a />` on web and a interactive `<Text />` which uses the `Linking` API on native. Routers like React Navigation have built-in [linking components](https://reactnavigation.org/docs/link) that you should use to move around your app.

### Common URL schemes

There are some URL schemes for core functionality that exist on every platform. The following is a non-exhaustive list, but covers the most commonly used schemes.

| Scheme           | Description                                  |
| ---------------- | -------------------------------------------- |
| `https` / `http` | Open web browser app, eg: `https://expo.dev` |
| `mailto`         | Open mail app, eg: `mailto:support@expo.dev` |
| `tel`            | Open phone app, eg: `tel:+123456789`         |
| `sms`            | Open SMS app, eg: `sms:+123456789`           |

On newer Android versions, include the appropriate queries in the **AndroidManifest.xml** to open links. This can be done by [creating a config plugin](/config-plugins/plugins-and-mods/#create-a-plugin). For example, the config plugin below will enable linking to phone and email apps:

```js my-plugin.js
const { withAndroidManifest } = require('@expo/config-plugins');

const withAndroidQueries = config => {
  return withAndroidManifest(config, config => {
    config.modResults.manifest.queries = [
      {
        intent: [
          {
            action: [{ $: { 'android:name': 'android.intent.action.SENDTO' } }],
            data: [{ $: { 'android:scheme': 'mailto' } }],
          },
          {
            action: [{ $: { 'android:name': 'android.intent.action.DIAL' } }],
          },
        ],
      },
    ];

    return config;
  });
};

module.exports = withAndroidQueries;
```

You can then [import the custom config plugin](/config-plugins/plugins-and-mods/#import-a-plugin) in your project's app config.

### Custom URL schemes

If you know the custom scheme for another app you can link to it. Some services provide documentation for deep linking. For example, the [Uber deep linking documentation](https://developer.uber.com/docs/riders/ride-requests/tutorials/deep-links/introduction#standard-deep-links) describes how to link directly to a specific pickup location and destination:

```shell
uber://?client_id=<CLIENT_ID>&action=setPickup&pickup[latitude]=37.775818&pickup[longitude]=-122.418028&pickup[nickname]=UberHQ&pickup[formatted_address]=1455%20Market%20St%2C%20San%20Francisco%2C%20CA%2094103&dropoff[latitude]=37.802374&dropoff[longitude]=-122.405818&dropoff[nickname]=Coit%20Tower&dropoff[formatted_address]=1%20Telegraph%20Hill%20Blvd%2C%20San%20Francisco%2C%20CA%2094133&product_id=a1111c8c-c720-46c3-8534-2fcdd730040d&link_text=View%20team%20roster&partner_deeplink=partner%3A%2F%2Fteam%2F9383
```

It's possible that the user doesn't have the Uber app installed, in which case you may want to open the App / Play Store, or let them know that they need to install it first. We recommend using the library [`react-native-app-link`](https://github.com/FiberJW/react-native-app-link) for these cases.

On iOS, `Linking.canOpenURL` requires additional configuration to query other apps' linking schemes. You can use the `expo.ios.infoPlist` key in your app config (**app.json**, **app.config.js**) to specify a list of schemes your app needs to query. For example:

```json app.json
{
  "expo": {
    "ios": {
      "infoPlist": {
        "LSApplicationQueriesSchemes": ["uber"]
      }
    }
  }
}
```

If you don't specify this list, `Linking.canOpenURL` may return `false` regardless of whether the device has the app installed. Note that this configuration can only be tested in [development builds](/develop/development-builds/introduction/) because it requires native changes that will not be applied when testing in [Expo Go][expo-go].

### Creating URLs

To save you the trouble of inserting a bunch of conditionals based on the environment that you're in and hardcoding URLs, we provide some helper methods in our extension of the `Linking` module. When you want to provide a service with a URL that it needs to redirect back into your app, you can call `Linking.createURL()` and it will resolve to the following:

- **Production and development builds**: `myapp://` (see [Linking to your app](#linking-to-your-app))
- **Development in Expo Go**: `exp://127.0.0.1:8081`.

When loading published updates in Expo Go, the URL that is created by `Linking.createURL()` depends on how the project was launched and shouldn't be relied upon to be consistent or stable. For applications that require a stable URL (authorization provider redirects, for example), you should use a development build with a custom scheme instead (see [Linking to your app](#linking-to-your-app)).

- **Project loaded via QR code or via menus in Expo Go**: `exp://u.expo.dev/[project-id]/group/[update-group-id]` or `exp://u.expo.dev/[project-id]/update/[update-id]`. The project ID is stable but the update group ID and update ID are not.

You can also change the returned URL by passing optional parameters into `Linking.createURL()`. These will be used by your app to receive data, which we will talk about in the next section.

To pass some data to an app, you can append it as a path or query string on your URL. `Linking.createURL()` will construct a working URL automatically for you. Example:

```js Linking.createURL() example
const redirectUrl = Linking.createURL('path/into/app', {
  queryParams: { hello: 'world' },
});
```

This will resolve into the following, depending on the environment:

- **Production and development builds**: `myapp://path/into/app?hello=world`
- **Development in Expo Go**: `exp://127.0.0.1:8081/--/path/into/app?hello=world`.

Again, when loading published updates in Expo Go this behavior is variable, but may look something like the following:

- **Published app in Expo Go**: `exp://u.expo.dev/[project-id]/group/[update-group-id]/--/path/into/app?hello=world`

> Notice in Expo Go that `/--/` is added to the URL when a path is specified. This indicates to Expo Go that the substring after it corresponds to the deep link path, and is not part of the path to the app itself.

### In-app browsers

The [`expo-linking`][expo-linking] API enables you to open a URL with the operating system's preferred application, you can use the [`expo-web-browser`](/versions/latest/sdk/webbrowser) module to open URLs with an in-app browser. In-app browsers are especially useful for secure [authentication](/guides/authentication).

<Terminal cmd={['$ npx expo install expo-web-browser']} />

<SnackInline label="WebBrowser vs Linking" dependencies={["expo-web-browser", "expo-linking"]}>

```jsx
import React from 'react';
import { Button, View, StyleSheet } from 'react-native';
import * as Linking from 'expo-linking';
import * as WebBrowser from 'expo-web-browser';

export default function App() {
  return (
    <View style={styles.container}>
      <Button
        title="Open URL with the system browser"
        onPress={() => Linking.openURL('https://expo.dev')}
        style={styles.button}
      />
      <Button
        title="Open URL with an in-app browser"
        onPress={() => WebBrowser.openBrowserAsync('https://expo.dev')}
        style={styles.button}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
  button: {
    marginVertical: 10,
  },
});
```

</SnackInline>

## Linking to your app

To link to your [development build](/develop/development-builds/introduction/) or standalone app, you need to specify a custom URL scheme for your app. You can register a scheme in your app config (**app.json**, **app.config.js**) by adding a string under the `scheme` key:

```json app.json
{
  "expo": {
    "scheme": "myapp"
  }
}
```

Once you build and install your app, you will be able to open it with links to `myapp://`.

> [Expo Prebuild](/workflow/prebuild) automatically adds the app's iOS bundle identifier/Android package as a URL scheme.

<ConfigReactNative abstract>

In **bare** apps, you can use the [`uri-scheme` package][n-uri-scheme] to easily add, remove, list, and open your URIs.

To make your native app handle `myapp://` simply run:

<Terminal cmd={['$ npx uri-scheme add myapp']} />

You should now be able to see a list of all your project's schemes by running:

<Terminal cmd={['$ npx uri-scheme list']} />

You can test it to ensure it works like this:

<Terminal
  cmd={[
    '# Rebuild the native apps, be sure to use an emulator',
    '$ yarn android',
    '$ yarn ios',
    '',
    '# Open a URI scheme',
    '$ npx uri-scheme open myapp://some/redirect',
  ]}
  cmdCopy="yarn android && yarn ios && npx uri-scheme open myapp://some/redirect"
/>

</ConfigReactNative>

### Linking to Expo Go

[Expo Go][expo-go] uses the `exp://` scheme, however, if we link to `exp://` without any address afterward, it will open the app to the home screen.

In development, your app will live at a url like `exp://127.0.0.1:8081`. When published, an experience will be hosted at a URL like `exp://u.expo.dev/[project-id]?channel-name=[channel-name]&runtime-version=[runtime-version]`, where `u.expo.dev/[project-id]` is the hosted URL that Expo Go fetches from.

You can test this mechanism in your mobile browser by searching `exp://u.expo.dev/F767ADF57-B487-4D8F-9522-85549C39F43F?channel-name=main&runtime-version=exposdk:45.0.0`, this will redirect to your experience in the Expo Go app.

By default `exp://` is replaced with `http://` when opening a URL in Expo Go. Similarly, you can use `exps://` to open `https://` URLs. `exps://` does not currently support loading sites with insecure TLS certificates.

### Handling links

Links that launched your app can be observed using the `Linking.useURL` React hook:

```tsx
import * as Linking from 'expo-linking';
import { Text } from 'react-native';

export default function App() {
  const url = Linking.useURL();

  return <Text>URL: {url}</Text>;
}
```

Behind the scenes, this hook uses the following imperative API methods:

1. The link that started the app is initially returned with: [`Linking.getInitialURL()`](/versions/latest/sdk/linking/#linkinggetinitialurl)
2. Any new links that were triggered while the app was already open are observed with: [`Linking.addEventListener('url', callback)`](/versions/latest/sdk/linking/#linkingaddeventlistenertype-handler)

For more information, see [`expo-linking`](/versions/latest/sdk/linking) API reference.

### Parsing URLs

Parse the **path**, **hostname**, and **query parameters** from a URL with the `Linking.parse()` function. Unlike other URL parsing methods, this function considers nonstandard implementations like [Expo Go linking](#linking-to-expo-go). Example:

```js
function App() {
  const url = Linking.useURL();

  if (url) {
    const { hostname, path, queryParams } = Linking.parse(url);

    console.log(
      `Linked to app with hostname: ${hostname}, path: ${path} and data: ${JSON.stringify(
        queryParams
      )}`
    );
  }

  return null;
}
```

## Testing URLs

> Adding schemes will require rebuilding your app.

You can open a URL like:

<Terminal
  cmd={[
    '# Custom builds',
    '$ npx uri-scheme open myapp://somepath/into/app?hello=world --ios',
    '',
    '# Expo Go in development (adjust the `127.0.0.1:8081` to match your dev server URL)',
    '$ npx uri-scheme open exp://127.0.0.1:8081/--/somepath/into/app?hello=world --ios',
  ]}
/>

You can _also_ open a URL by searching for it on the device's native browser. For example, opening Safari on iOS and typing `exp://` then searching will prompt you to open [Expo Go][expo-go] (if installed).

## Next steps

<BoxLink
  title="Deep linking"
  description="Setup iOS universal links and Android deep links."
  href="/guides/deep-linking"
  Icon={BookOpen02Icon}
/>

<BoxLink
  title="Authentication"
  description="Use linking to implement web-based authentication."
  href="/guides/authentication"
  Icon={BookOpen02Icon}
/>

<BoxLink
  title="Routing"
  description="Setup React Navigation linking for in-app routing."
  href="https://reactnavigation.org/docs/configuring-links"
  Icon={BookOpen02Icon}
/>

[expo-go]: https://expo.dev/go
[n-uri-scheme]: https://www.npmjs.com/package/uri-scheme
[expo-linking]: /versions/latest/sdk/linking
